﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;
using InversPong.Model;
using InversPong.Utilities;

namespace InversPong
{
    public class GameController
    {

        public Model.Game Game { get; private set; }

        private TimeSpan timerSinceLastBomb = TimeSpan.Zero;

        private TimeSpan timerSinceLastPowerUp = TimeSpan.Zero;

        public GameController(Model.Game game)
        {
            Game = game;
        }


        public void HandleInput(KeyboardState keyboardState)
        {
            Player[] players = Game.GetPlayers();
            foreach(Player player in players) 
            {
                Vector2 movement = Vector2.Zero;
                bool testUp;
                bool testDown;
                int inverse = 1;
                testUp = (player.Bat.Position.Y - 1 > 0);
                testDown = (player.Bat.Position.Y + player.Bat.Height + 1 <= Game.Map.Height);
                if (player.Inversed)
                {
                    inverse = -1;
                    testUp = (player.Bat.Position.Y + player.Bat.Height - 1 <= Game.Map.Height);
                    testDown = (player.Bat.Position.Y + 1 > 0);
                }

                if (keyboardState.IsKeyDown(player.Up) && testUp)
                {
                    movement.Y -= inverse;
                }

                if (keyboardState.IsKeyDown(player.Down) && testDown)
                {
                    movement.Y += inverse;
                }


                if (movement.Length() > 1)
                {
                    movement.Normalize();
                }

                player.Bat.Position += movement * (float)player.Bat.Speed;
            }
            
        }
        public void Update(GameTime gameTime)
        {
            if (Game.GameStatus == InversPong.Model.Game.Status.Running)
            {
                IList<MovableObject> toBeDeletedObj = new List<MovableObject>();
                Player[] players = Game.GetPlayers();
                foreach (MovableObject obj in Game.Map.Movables)
                {
                    foreach (Player player in players)
                    {
                        if (HandleCollision(obj, player) || CheckExit(obj, Game.Map))
                        {
                            toBeDeletedObj.Add(obj);
                        }
                    }
                    obj.Move();
                }

                foreach (Player player in players)
                {
                    IList<PowerUp> powerUps = player.Bat.GetPowerUps();
                    foreach (PowerUp powerUp in powerUps)
                    {
                        if (Game.GameStatus == InversPong.Model.Game.Status.GameOver)
                        {
                            ApplyPowerUp(player, powerUp, -1);
                        }
                        else
                        {
                            powerUp.Timer -= gameTime.ElapsedGameTime;
                            if (powerUp.Timer < TimeSpan.Zero)
                            {
                                powerUp.Timer = TimeSpan.Zero;
                                ApplyPowerUp(player, powerUp, -1);
                            }
                        }
                    }
                    player.Bat.RemoveUnavailablesPowerUps();

                }

                foreach (MovableObject obj in toBeDeletedObj)
                {
                    Game.Map.RemoveMovableObject(obj);
                }


                if (timerSinceLastBomb >= Game.Level.RandomBombTimer())
                {
                    Game.Map.GenerateRandomBomb();
                    timerSinceLastBomb = TimeSpan.Zero;
                }

                if (timerSinceLastPowerUp >= Game.Level.RandomPowerUpTimer())
                {
                    Game.Map.GenerateRandomPowerUp();
                    timerSinceLastPowerUp = TimeSpan.Zero;
                }

                timerSinceLastBomb += gameTime.ElapsedGameTime;
                timerSinceLastPowerUp += gameTime.ElapsedGameTime;
            }
        }

        private void ApplyPowerUp(Player player, PowerUp powerUp, int action)
        {
            switch (powerUp.Type)
            {
                case PowerUp.Types.Inverse:
                    player.Inversed = !player.Inversed;
                    break;
                case PowerUp.Types.Escargot:
                    player.Bat.Speed -= 3*action;
                    break;
                case PowerUp.Types.Moteur:
                    player.Bat.Speed += 3*action;
                    break;
                case PowerUp.Types.Size_Dim:
                    player.Bat.Height -= 40*action;
                    break;
                case PowerUp.Types.Viagra:
                    player.Bat.Height += 40*action;
                    break;

            }
        }
        private bool HandleCollision(MovableObject obj, Player player)
        {
            if (isColliding(obj, player.Bat))
            {
                if (obj is Bomb)
                {
                    if (!player.Bat.ContainsPowerUp(PowerUp.Types.Invulnerability))
                    {
                        if (LifeLosed != null) LifeLosed(this, new EventArgs<Player>(player));
                        player.LifeLosed();
                        player.Bat.AddPowerUp(new PowerUp(Vector2.Zero, Vector2.Zero, PowerUp.Types.Invulnerability, Game.Level.InvunerabilityTimer()));
                        if (!player.isAlive())
                        {
                            Game.GameStatus = Model.Game.Status.GameOver;
                            if (GameFinished != null) GameFinished(this, new EventArgs<Player>(player));
                        }
                    }
                }
                else if (obj is PowerUp)
                {

                    PowerUp powerUp = (PowerUp)obj;
                    if (player.Bat.ContainsPowerUp(powerUp.Type))
                    {
                        player.Bat.RemovePowerUp(powerUp.Type);
                    }
                    else
                    {
                        ApplyPowerUp(player, powerUp, 1);
                    }

                    TimeSpan powerUpTimer = Game.Level.PowerUpTimer();
                    if (powerUp.Type == PowerUp.Types.Invulnerability)
                    {
                        powerUpTimer = Game.Level.InvunerabilityTimer();
                    }
                    powerUp.Catched(powerUpTimer);

                    if (PowerUpPicked != null) PowerUpPicked(this, new EventArgs<PowerUp>(powerUp));
                    player.Bat.AddPowerUp(powerUp);
                }
                return true;
            }
            else
            {
                return false;
            }
        }

        private bool isColliding(InversPong.Model.Object obj1, InversPong.Model.Object obj2)
        {
            return obj1.BoundingBox.Intersects(obj2.BoundingBox);
        }

        private bool CheckExit(MovableObject obj, Map map)
        {
            Vector2 nextPosition = obj.GetNextPosition();
            Vector2 velocity = obj.Velocity;
            if ((nextPosition.X+obj.Width < 0) || ((nextPosition.X) >= Game.Map.Width))
            {
                return true;
            }

            if ((nextPosition.Y < 0) || ((nextPosition.Y + obj.Height) >= Game.Map.Height))
            {
                velocity.Y = -velocity.Y;
            }
            obj.Velocity = velocity;
            return false;
        }

        /// <summary>
        /// Fired when a player lose a life.
        /// </summary>
        public event EventHandler<EventArgs<Player>> LifeLosed;

        /// <summary>
        /// Fired when a powerUp is picked.
        /// </summary>
        public event EventHandler<EventArgs<PowerUp>> PowerUpPicked;

        /// <summary>
        /// Fired when a gamle is finished.
        /// </summary>
        public event EventHandler<EventArgs<Player>> GameFinished;
    }

}
